#include "luster_log.hh"

#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>

#include "luster_config.hh"
#include "luster_status.hh"

namespace luster {

#if luster_CFG(DEBUGLOG)
LogLevel MIN_LOGLEVEL = LOG_DEBUG;
#else
LogLevel MIN_LOGLEVEL = LOG_INFO;
#endif

Logger LOGGER{};
Logger FATAL_LOGGER{.fatal = true};

static void LogHead_Debug(LogLevel level, char const *file, uintptr_t line) {
	struct timespec timebuf;
	clock_gettime(CLOCK_REALTIME, &timebuf);

	static char const *name[] = {
#define _(M_NAME) #M_NAME,
		LOGLEVEL_MAP(_)
#undef _
	};

	fprintf(stderr,
		"[%ld.%06ld] %s:%" PRIuPTR "(%s): ", (long)timebuf.tv_sec,
		(long)timebuf.tv_nsec / 1000, file, line, name[level]);
}

static void LogHead_NoDebug(
	LogLevel level, char const *file__, uintptr_t line__) {
	luster_UNUSED(file);
	luster_UNUSED(line);

	static char const *name[] = {
#define _(M_NAME) #M_NAME,
		LOGLEVEL_MAP(_)
#undef _
	};

	fprintf(stderr, "%s: ", name[level]);
}

void LogHead(LogLevel level, char const *file, uintptr_t line) {
#if luster_CFG(DEBUGLOG)
	return LogHead_Debug(level, file, line);
#else
	return LogHead_NoDebug(level, file, line);
#endif
}

static void vlogprint(LogLevel level, char const *file, uintptr_t line,
	char const *fmt, va_list ap);

void LogWrite(LogLevel level, char const *file, uintptr_t line, char const *fmt,
	...) {
	if (level < MIN_LOGLEVEL)
		return;

	va_list ap;
	va_start(ap, fmt);
	vlogprint(level, file, line, fmt, ap);
	va_end(ap);
}

static void vlogprint(LogLevel level, char const *file, uintptr_t line,
	char const *fmt, va_list ap) {
	LogHead(level, file, line);
	vfprintf(stderr, fmt, ap);
	fprintf(stderr, "\n");
}

} // namespace luster
